"""
Component library. 

Copyright (c) 2008 Rune M. Friborg, runef@diku.dk.
See LICENSE.txt for licensing details (GPLv2).
"""

import os
import cspxml
from plugins.builder import *


class Component:
    """ A class for holding a component
    """

    def __init__(self, lib, process_node, filename):
        """ Constructor initialises component from an analysis of process node
        """

        att= cspxml.getAttrList(process_node)
        self.name = str(att['name'])
                
        # Package
        if (att.has_key('package')):
            self.package = att['package']
        else:
            self.package = 'Unknown'

        self.componentLib = lib
                                        
        # Get plugin values
        self.plugin_attr = lib.pluginman.loadAttr(node_PROCESS_CONTAINER, att, {})

        # csp filename
        self.componentFile = filename
                                        
        # Get channelends and do a recursive lookup for channelend type
        self.inputs = []
        self.outputs = []
        for channelend in process_node.childNodes:
            if (channelend.nodeName == 'channelend'):
                att_channelend = cspxml.getAttrList(channelend)

                # Recursive lookup
                t = self._get_type(process_node, att_channelend['direction'], att_channelend['target'])
                try:
                    t = eval(t)
                except:
                    t = ("unknown", -1)

                if not t in lib.known_types:
                    lib.known_types.append(t)

                if (att_channelend['direction'] == 'in'):
                    self.inputs.append((att_channelend['name'], t))
                else:
                    self.outputs.append((att_channelend['name'], t))

    
    def _get_type(self, node, d, target):
        dot_idx = target.index(".")
        before_dot = target[:dot_idx]
        after_dot = target[dot_idx+1:]
        for p in node.childNodes:
            if (p.nodeName == 'process'):
                p_att = cspxml.getAttrList(p)
                if (p_att['name'] == before_dot):
                    for c in p.childNodes:
                        if c.nodeName == 'channelend':
                            c_att = cspxml.getAttrList(c)
                            if (c_att['direction'] == d and c_att['name'] == after_dot):
                                if c_att.has_key('type'):
                                    return c_att['type']
                                else:
                                    return "((\"unknown\"), -1)"

            if (p.nodeName == 'processlink'):
                p_att = cspxml.getAttrList(p)
                if (p_att['name'] == before_dot):

                    # Find location and parse file.
                    document = None
                    for path in self.componentLib.workspacePathList + [sys.path[0]]:
                        pathAndFilename = path + '/' + p_att['file']
                        if os.path.exists(pathAndFilename):
                            document = cspxml.parseFile(pathAndFilename)
                            break

                    
                    if not document:
                        print p_att['file'] + ' could not be found in componentlib _get_type()'
                        break

                    for doc_node in document.childNodes:
                        if (doc_node.nodeName == "doc"):
                            for p_node in doc_node.childNodes:
                                if (p_node.nodeName == "process"):
                                    
                                    # Translate target
                                    for c in p_node.childNodes:
                                        if c.nodeName == 'channelend':
                                            c_att = cspxml.getAttrList(c)
                                            if (c_att['direction'] == d and c_att['name'] == after_dot):
                                                return self._get_type(p_node, d, c_att['target'])

        # Search failed!
        return None



class ComponentLib:
    """ A class for loading and administrating components
    """

    def __init__(self, pluginmanager):
        """ Standard constructor.
        """

        self.pluginman = pluginmanager
        self.workspacePathList = self.pluginman.getList(pathlist_COMPONENTS)

        self.libData = {}
        self.known_types = []

        self.load()

    def reload(self):
        self.libData = {}
        self.load()


    # Load components into lib
    def load(self):
        # Parse workspace folders
        for path in self.workspacePathList:
            self.parse_files_in(path)


    def parse_files_in(self, toppath):
        for root, dirs, files in os.walk(toppath):
            if (root == toppath):
                for filename in files:
                    if (filename[-4:] == '.csp'):
                        name = filename[0:-4]
                        # Parse files and add found components.
                        document = cspxml.parseFile(toppath + '/' + filename)
                        for e1 in document.childNodes:
                            if (e1.nodeName == "doc"):
                                for e2 in e1.childNodes:
                                    if (e2.nodeName == "process"):
                                        
                                        cObj = Component(self, e2, toppath + '/' + filename)
                                    
                                        # Adding to libData
                                        if (cObj.name != ''):
                                            self.libData[cObj.name] = cObj

                        document.unlink()
                    

    def strList(self):
        L = []
        for x in self.libData:
            L.append(x)
        return L

    def getPackageList(self):
        D = {}
        for x in self.libData:
            if (D.has_key(self.libData[x].package)):
                D[self.libData[x].package][x] = self.libData[x]
            else:
                D[self.libData[x].package] = {x:self.libData[x]}
        return D

    def getTypes(self):
        return self.known_types

    def componentExists(self, componentStr):
        if (componentStr in self.libData):
            return True
        else:
            return False
        

    def getComponent(self, componentStr):
        if (componentStr in self.libData):
            #
            return self.libData[componentStr]
        else:
            return None
